/*  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "luge-node.hh"
#include "luge-pad.hh"
#include "luge-util.hh"
#include "luge-color.hh"
#include "luge-logging.hh"
#include "options.hh"

#include <iostream>
#include <sstream>
#include <gegl.h>
#include <gegl-utils.h>
#include <lualib.h>
#include <lauxlib.h>
#include <lua.h>

namespace {
/**
 * register the global functions (constructors).
 */
static void luge_init (lua_State *ls)
{
  lua_pushcclosure (ls, luge::node_construct, 0);
  lua_setglobal (ls, "node");
  lua_pushcclosure (ls, luge::color_construct, 0);
  lua_setglobal (ls, "color");
}

/**
 * enable opencl if on, else disable it.
 */
static void enable_opencl (bool on)
{
  GeglConfig *config = gegl_config ();
  g_object_set (config, "use-opencl", int (on), (char *) 0);
}

/**
 * handle all options that are executed without running the script.
 * Return true, if there were any.
 * prog is the program name (for help option), ec is set to the
 * exit status.
 */
static bool no_script_options
  (const std::string &prog, const luge::options &opts, int &ec)
{
  if (opts.interpreter_opt.found_errors) {
    ec = 1;
    return true;
  } else if (opts.interpreter_opt.help) {
    luge::usage (prog);
    ec = 0;
    return true;
  } else if (opts.interpreter_opt.display_version) {
    std::cout << "1\n";
    ec = 0;
    return true;
  } else {
    ec = 0;
    return false;
  }
}

/**
 * create the vm and execute the script.
 */
static void execute_script (const luge::options &opts, int &exit_code)
{
  /*
   * initialization of the vm.
   */
  lg::init_logging (opts.interpreter_opt.verbosity);
  lua_State *ls = luaL_newstate ();
  luge_init (ls);
  luaL_openlibs (ls);
  enable_opencl (opts.interpreter_opt.enable_opencl);
  /*
   * execute the script.
   */
  if (opts.interpreter_opt.script_name.empty ()) {
    std::cerr << "error: required script-name missing.\n";
    exit_code = 1;
  } else {
    luge::bind_args (ls, opts.script_opt);
    std::string program_name = opts.interpreter_opt.script_name;
    exit_code = luaL_dofile (ls, program_name.c_str ());
    if (exit_code != 0) {
      lg::error () << "error: " << lua_tostring (ls, -1) << '\n';
    }
  }
  lua_close (ls);
}

} /* local namespace */

int main (int argc, char **argv)
{
  /*
   * parse options
   */
  gegl_init (&argc, &argv);
  luge::options opts (luge::parse_args (argc, argv));
  /*
   * check if the options suggest to exit early (i.e. either
   * there were errors parsing the options, or usage requested).
   */
  int exit_code;
  if (no_script_options (argv[0], opts, exit_code)) {
    return exit_code;
  } else {
    execute_script (opts, exit_code);
    return exit_code;
  }
  return exit_code;
}
